/*
 * 代号：凤凰
 * http://www.jphenix.org
 * 2014-06-12
 * V4.0
 */
package com.jphenix.share.util;

//#region 【引用区】
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.WritableByteChannel;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
import com.jphenix.share.lang.SString;
import com.jphenix.share.lang.SortVector;
import com.jphenix.share.tools.FileCopyTools;
import com.jphenix.standard.docs.ClassInfo;
import com.jphenix.standard.exceptions.MsgException;
//#endregion

//#region 【类说明区】
/**
 * 文件处理静态类
 * 
 * 增加脚本文件夹中，如果存在文件名为 .disabled 的文件，则不扫描当前文件夹以及其子文件夹中的脚本文件。
 * 然后会自动删除对应的脚本编译类，不执行这些类。 这个功能通常用在本地开发环境，有些文件夹中的脚本需要
 * 连接指定的数据库，而本地环境没有。导致大量的的脚本报错。增加了这个文件，即可在本地环境不启动这些脚本
 * 还不影响本地SVN提交（需要将本地的.disabled 文件设置为svn忽略）
 * 
 * 
 * 2018-05-28 增加了写入文件内容的方法 addContent putContent
 * 2018-05-29 增加了搜索压缩包中文件的方法，可以传入压缩包对象
 * 2018-06-13 增加了将指定文件打包到ZIP文件中
 * 2018-06-26 完善了获取全路径的方法中，判断目标路径信息是否为全路径
 * 2018-06-27 修改了获取全路径中，判断是否为绝对路径的错误
 * 2018-06-29 增加了为指定文件创建文件夹的方法。避免创建文件时，保存该文件的文件夹不存在的问题
 * 2018-07-25 修改了获取指定路径的全路径方法，在linux中返回错误的问题
 * 2018-12-12 增加了createFile(file)方法，并修改了createFile(path)方法的处理逻辑
 * 2019-01-16 增加了构建压缩包文件对象方法。同时完善了报错信息
 * 2019-08-09 增加了打包文件方法中，可以单独设置压缩包内文件路径的方法createZipFile
 * 2020-03-09 修改了获取全路径方法getAllFilePath的返回值，如果是windows系统，去掉了返回值路径开头的斜杠
 * 2020-08-29 修改了返回全路径方法因为全路径一级文件夹名跟虚拟路径一级文件夹名相同导致错判的问题
 * 2020-08-31 返回全路径的方法，轻易不能动，伤筋动骨。之前是因为子文件夹的名字跟一级文件夹名字相同，导致虚拟路径误判成根路径。避免子文件夹名字跟根文件夹名字相同
 * 2020-09-09 还原了获取全路径方法
 * 2024-03-27 在适配JDK17时，发现windows环境中的路径必须为\\格式，有些地方使用/会报错，所以在该类中，判断如果是\\分隔符，严格使用该分隔符，不再统一使用 / 分割符
 * 2024-04-19 修改了外部调用fixUpPath返回值错误
 * 2024-06-04 修改了获取全路径方法 getAllFilePath 的判断错误问题
 * 2024-06-19 新增unzip方法，解压缩指定文件
 * 2024-08-17 修改了一处错误
 * 
 * @author 刘虻
 * 2009-12-7 下午01:32:09
 */
//#endregion
@ClassInfo({"2024-08-17 23:20","文件处理静态类"})
public class SFilesUtil {
    
	//#region ZipOneInputStream 单个压缩文件内部元素读入流
    /**
     * 单个压缩文件内部元素读入流
     * 在读取一个压缩文件后，执行关闭方法，顺便讲压缩文件也关闭
     * @author 马宝刚
     * 2015年7月16日
     */
    public static class ZipOneInputStream extends InputStream {
        
        private InputStream zipIs   = null; // 单个压缩文件元素读入流
        private ZipFile     zipFile = null; // 压缩文件对象
        
        /**
         * 构造函数
         * @author 马宝刚
         */
        public ZipOneInputStream(InputStream zipIs,ZipFile zipFile) {
            super();
            this.zipIs   = zipIs;
            this.zipFile = zipFile;
        }
        
        /* （非 Javadoc）
         * @see java.io.InputStream#read()
         */
        @Override
        public int read() throws IOException {
            return zipIs.read();
        }
        
        /**
         * 关闭流，顺便把压缩文件对象也关闭掉
         */
        @Override
        public void close() throws IOException {
            try {
                zipIs.close();
            }catch(Exception e) {}
            try {
                zipFile.close();
            }catch(Exception e) {}
        }
    }
    //#endregion
    
    //#region getFileInputStreamByUrl(fileUrl,basePath) 通过文件路径获取文件读入流
    /**
     * 通过文件路径获取文件读入流
     * @author 刘虻
     * 2008-7-26下午05:34:35
     * @param fileUrl 文件路径获取文件读入流
     * @return 文件读入流
     * @throws Exception 执行发生异常
     */
    public static InputStream getFileInputStreamByUrl(
            String fileUrl,String basePath) throws Exception {
        if (fileUrl==null) {
            throw new IOException("Not Find The File Path Or URL String");
        }
        fileUrl = getAllFilePath(fileUrl,basePath); //获取全路径
        if (isZipUrl(fileUrl)) {
            return getZipFileInputStreamByUrl(null,fileUrl);
        }else {
            return new FileInputStream(getFileByName(fileUrl,basePath));
        }
    }
    //#endregion
    
    //#region getFileByName(filePathStr,basePath) 通过URL方式获得文件对象
    /**
     * 通过URL方式获得文件对象
     * @author 刘虻
     * @param filePathStr 文件相对路径
     * @return 对应的文件对象
     * @exception IOException 获取文件时发生异常
     * 2006-4-8  14:01:57
     */
    public static File getFileByName(
            String filePathStr,String basePath) throws IOException {
        //构造返回值
        File reFile = new File(getAllFilePath(filePathStr,basePath));
        if (!reFile.exists()) {
            throw new IOException("No Find The File By Path:["
                    +filePathStr+"] AllPath:["
                    +reFile.getPath()+"] BasePath:["+basePath+"]");
        }
        return reFile;
    }
    //#endregion
    
    //#region getFileLastModified(filePath) 获取文件最后更新时间
    /**
     * 获取文件最后更新时间
     * @author 刘虻
     * 2008-7-27下午08:47:36
     * @param filePath 文件路径
     * @return 文件最后更新时间
     */
    public static long getFileLastModified(String filePath) {
        if (filePath==null) {
            return 0;
        }
        if (isZipUrl(filePath)) {
            return getZipFileTime(filePath);
        }else {
            return (new File(filePath)).lastModified();
        }
    }
    //#endregion
    
    //#region fixPathByOs(path) 按照运行系统整理路径和整合路径
    /**
     * 按照运行系统整理路径和整合路径
     * @param path 整理一个或多个路径，如果是多个路径，将多个路径整合成1个路径
     * @return     整合后的路径
     * 2024年3月27日
     * @author MBG
     */
    public static String fixPathByOs(String... path) {
    	// 构建返回值
    	StringBuilder reSbd    = new StringBuilder();
    	// 上一个路径参数
    	String        lastPath = null;
    	if(path==null) {
    		return "";
    	}
    	boolean       isUrl = false; // 是否为URL
    	if(path.length>0 && (path[0].toLowerCase().startsWith("file:") || path[0].toLowerCase().startsWith("zip:"))) {
    		isUrl = true;
    	}
    	// 是否为windows路径分隔符
    	boolean winSep = File.separator.equals("\\");
    	for(int i=0;i<path.length;i++) {
    		if(path[i]==null || path[i].length()<1) {
    			continue;
    		}
        	if(winSep && !isUrl) {
        		// Windows 系统
        		path[i] = path[i].replaceAll("/","\\\\");
        		if(lastPath==null && path[i].startsWith("\\\\")) {
        			// 服务器根路径 \\192.168.0.1\path1
        			
            		// 除去重复路径分隔符 \\ 改为 \
            		path[i] = path[i].replaceAll("\\\\\\\\","\\\\");
            		path[i] = "\\"+path[i];
        		}else {
            		// 除去重复路径分隔符 \\ 改为 \
            		path[i] = path[i].replaceAll("\\\\\\\\","\\\\");
            		if(path[i].startsWith("\\")) {
            			path[i] = path[i].substring(1);
            		}
        		}

        		if(lastPath!=null && !lastPath.endsWith("\\")) {
        			reSbd.append("\\");
        		}
        		reSbd.append(path[i]);
        		lastPath = path[i];
        	}else {
        		// 非Windows系统
        		
        		path[i] = path[i].replaceAll("\\\\","/");
        		
        		if(!isUrl || lastPath!=null) {
            		// 除去重复分隔符
            		path[i] = path[i].replaceAll("//","/");
        		}
        		
        		if(lastPath!=null && !lastPath.endsWith("/")) {
        			if(!path[i].startsWith("/")) {
        				reSbd.append("/");
        			}
        		}
        		reSbd.append(path[i]);
        		lastPath = path[i];
        	}
    	}
    	return reSbd.toString();
    }
    //#endregion
    
    //#region getAllFilePath(dummyPath,basePath) 获取完整路径
    /**
     * 获取完整路径
     * @author 刘虻
     * 2007-6-18下午04:21:49
     * @param dummyPath 相对路径
     * @param basePath 类根路径
     * @return 完整路径
     */
    public static String getAllFilePath(String dummyPath,String basePath) {
        if(basePath==null) {
            basePath = "";
        }
        
        basePath = fixPathByOs(basePath);
        
        if (dummyPath==null || dummyPath.length()<1) {
            return basePath;
        }
        dummyPath = fixPathByOs(dummyPath);
       
        //是否为根路经
        boolean isBase = false;
        if (dummyPath.startsWith("\\\\")
                || dummyPath.startsWith("zip:")
                || dummyPath.startsWith("file:")
                || dummyPath.indexOf(":\\")==1
                || dummyPath.indexOf(":\\")==2
                || (basePath.length()>0 
                // 注意： 如果相对路径中的第一个文件夹的名字跟basePath中第一个文件夹的名字相同，就判断错误了
                //        && getFristPath(dummyPath).equals(getFristPath(basePath)))) {
                     && dummyPath.startsWith(basePath))) {
            isBase = true;
        }
        if (!isBase) {
            if (dummyPath.startsWith(File.separator)) {
                dummyPath = dummyPath.substring(1);
            }
            if(basePath.endsWith(File.separator)) {
            	dummyPath = basePath+dummyPath;
            }else {
            	dummyPath = basePath+File.separator+dummyPath;
            }
        }
        dummyPath = fixUpPath(dummyPath,true);
        /*
        // 效率低，没必要
        if(!(new File(dummyPath)).exists()) {
            //搜索资源获取绝对路径
            URL pathUrl = null;
            if(dummyPath.startsWith("/")) {
                pathUrl = 
                    SFilesUtil.class
                        .getClassLoader()
                            .getResource(
                                    dummyPath.substring(1,dummyPath.length()));
            }else {
                pathUrl = 
                    SFilesUtil.class
                        .getClassLoader()
                            .getResource(dummyPath);
            }
            if(pathUrl==null) {
                return dummyPath;
            }
            return StringUtil.getURLDecoding(pathUrl.getPath());
        }
        */
        return dummyPath;
    }
    //#endregion
    
    //#region getFileList(sv,searchPath,fileHead,extName,returnSubPath) 搜索指定文件夹中指定的文件，将符合条件的路径放入序列中 （不递归搜索子文件夹中的文件）
    /**
     * 搜索指定文件夹中指定的文件，将符合条件的路径放入序列中 （不递归搜索子文件夹中的文件）
     * 刘虻
     * 2009-12-7 下午04:11:38
     * @param sv 排序序列
     * @param searchPath 搜索路径（全路径）
     * @param fileHead 搜索符合文件头名的文件（不分大小写）
     * @param returnSubPath 是否返回相对路径
     * @throws Exception 执行发生异常
     */
    public static void getFileList(
            SortVector<String> sv
            ,String searchPath
            ,String fileHead
            ,final String extName
            ,boolean returnSubPath) throws Exception {
    	getFileList(sv,searchPath,fileHead,extName,returnSubPath,false);
    }
    //#endregion
    
    //#region getFileList(sv,searchPath,fileHead,extName,returnSubPath,checkDisabledFile) 搜索指定文件夹中指定的文件，将符合条件的路径放入序列中 （不递归搜索子文件夹中的文件）
    /**
     * 搜索指定文件夹中指定的文件，将符合条件的路径放入序列中 （不递归搜索子文件夹中的文件）
     * 刘虻
     * 2009-12-7 下午04:11:38
     * @param sv 排序序列
     * @param searchPath 搜索路径（全路径）
     * @param fileHead 搜索符合文件头名的文件（不分大小写）
     * @param returnSubPath 是否返回相对路径
     * @param checkDisabledFile true如果当前文件夹中包含名为 .disabled 文件，则不扫描当前文件夹中的所有文件（包含子文件夹）
     * @throws Exception 执行发生异常
     */
    public static void getFileList(
            SortVector<String> sv
            ,String searchPath
            ,String fileHead
            ,final String extName
            ,boolean returnSubPath
            ,boolean checkDisabledFile) throws Exception {
        //导入参数合法化
        if (sv==null) {
            return;
        }
        if (searchPath==null) {
            searchPath = "";
        }
        if ("//".equals(searchPath)) {
            sv.addAll(getDriveArrayList());
            return;
        }
        //扩展名序列
        List<String> extNameList = null;
        if(extName==null || extName.length()<1) {
            extNameList = new ArrayList<String>();
        }else {
            extNameList = 
                BaseUtil.splitToList(extName.toLowerCase(),";");
        }
        if (isZipUrl(searchPath)) {
            //被认为是压缩包
            searchZipFilePath(sv,searchPath,fileHead,extNameList,returnSubPath);
        }else {
            //搜索操作系统指定路径下的文件
            doSetFilePath(sv,searchPath,"",fileHead,extNameList,returnSubPath,checkDisabledFile);
        }
    }
    //#endregion

    //#region getFileList(filePathList,searchPath,fileHead,extName,incChild,returnSubPath) 搜索指定文件夹中指定的文件，将符合条件的路径放入序列中
    /**
     * 搜索指定文件夹中指定的文件，将符合条件的路径放入序列中
     * 刘虻
     * 2009-12-7 下午04:11:38
     * @param filePathList 符合条件的文件路径序列
     * @param searchPath 搜索路径（全路径）
     * @param fileHead 搜索符合文件头名的文件（不分大小写）
     * @param extName 扩展名
     * @param incChild 是否包含子文件夹
     * @param returnSubPath 是否返回相对路径
     * @throws Exception 执行发生异常
     */
    public static void getFileList (
            List<String> filePathList
            ,String searchPath
            ,String fileHead
            ,final String extName
            ,boolean incChild
            ,boolean returnSubPath) throws Exception {
    	getFileList(filePathList,searchPath,fileHead,extName,incChild,returnSubPath,false);
    }
    //#endregion
    
    //#region getFileList(filePathList,searchPath,fileHead,extName,incChild,returnSubPath,checkDisabledFile) 搜索指定文件夹中指定的文件，将符合条件的路径放入序列中
    /**
     * 搜索指定文件夹中指定的文件，将符合条件的路径放入序列中
     * 刘虻
     * 2009-12-7 下午04:11:38
     * @param filePathList 符合条件的文件路径序列
     * @param searchPath 搜索路径（全路径）
     * @param fileHead 搜索符合文件头名的文件（不分大小写）
     * @param extName 扩展名
     * @param incChild 是否包含子文件夹
     * @param returnSubPath 是否返回相对路径
     * @param checkDisabledFile true如果当前文件夹中包含名为 .disabled 文件，则不扫描当前文件夹中的所有文件（包含子文件夹）
     * @throws Exception 执行发生异常
     */
    public static void getFileList (
            List<String> filePathList
            ,String searchPath
            ,String fileHead
            ,final String extName
            ,boolean incChild
            ,boolean returnSubPath
            ,boolean checkDisabledFile) throws Exception {
        //导入参数合法化
        if (filePathList==null) {
            return;
        }
        if (searchPath==null) {
            searchPath = "";
        }
        if ("\\\\".equals(searchPath)) {
            filePathList.addAll(getDriveArrayList());
            return;
        }
        //扩展名序列
        List<String> extNameList = null;
        if(extName==null || extName.length()<1) {
        	extNameList = new ArrayList<String>();
        }else {
        	extNameList = BaseUtil.splitToList(extName.toLowerCase(),";");
        }
        if (isZipUrl(searchPath)) {
            //被认为是压缩包
            searchZipFilePath(filePathList,searchPath,fileHead,extNameList,returnSubPath);
        }else {
            //搜索操作系统指定路径下的文件
            doSetFilePath(filePathList,searchPath,"",fileHead,extNameList,incChild,returnSubPath,checkDisabledFile);
        }
    }
    //#endregion
    
    //#region getContextClassLoaderResource(filePath) 通过当前类加载器确定文件的绝对路径
    /**
     * 通过当前类加载器确定文件的绝对路径
     * @author 刘虻
     * 2009-7-17上午10:58:38
     * @param filePath 文件相对路径
     * @return 文件绝对路径
     * @throws Exception 执行发生异常
     */
    public static String getContextClassLoaderResource(
                                String filePath) throws Exception {
        //替换字符
        filePath = BaseUtil.swapString(filePath,"\\","/");
        if (filePath.startsWith("/")) {
            //去掉开头的/ 否则下一步获取不出真实路径
            filePath = filePath.substring(1);
        }
        try {
            //获取源
            URL res = 
                Thread
                    .currentThread()
                        .getContextClassLoader()
                            .getResource(filePath);
            if (res!=null) {
                //获取真实路径
                filePath = res.toString();
                try {
                    //从Class中获取到的路径都是UTF-8编码格式的
                    filePath = URLDecoder.decode(filePath,"UTF-8");
                }catch(Exception e) {}
            }
        }catch(Exception e) {
            e.printStackTrace();
            throw new Exception("Not Find The Config File Path:["+filePath+"]");
        }
        if (filePath.startsWith("file:")) {
            filePath = filePath.substring(5);
            if (filePath.startsWith("/")) {
                filePath = filePath.substring(1);
            }
        }
        if (filePath.indexOf(":")<0 && !filePath.startsWith("/")) {
            //在Linux或Unix根路径开头必须为 /
            filePath = "/"+filePath;
        }
        return filePath;
    }
    //#endregion
    
    //#region getFilePath(filePathStr) 从文件全路径中获取文件路径
    /**
     * 从文件全路径中获取文件路径
     * @author 刘虻
     * 2006-9-13下午02:54:15
     * @param filePathStr 文件全路径
     * @return 文件路径
     */
    public static String getFilePath(String filePathStr) {
        //导入参数合法化
        if (filePathStr == null 
                || filePathStr.length()<1) {
            return "";
        }
        //整理路径
        filePathStr = fixPathByOs(filePathStr);
        int lastPoint;
        if(isZipUrl(filePathStr)) {
            lastPoint = filePathStr.indexOf("!");
            if(lastPoint>0) {
                filePathStr = filePathStr.substring(0,lastPoint);
            }
        }
        if(filePathStr.startsWith("file:/")) {
            filePathStr = filePathStr.substring(5);
        }else if(filePathStr.startsWith("zip:/")) {
            filePathStr = filePathStr.substring(4);
        }
        /*
         * 如果要检测的文件没有扩展名就热闹了
         * 看起来就像文件夹名，如果文件夹名中间带个点，也挺热闹
         */
        File checkFile = new File(filePathStr);
        if(checkFile.isDirectory()) {
            return filePathStr;
        }
        lastPoint = filePathStr.lastIndexOf(File.separator); //最后的位置
        if (lastPoint>0) {
            return filePathStr.substring(0,lastPoint+1);
        }
        return "";
    }
    //#endregion
    
    //#region getFilePath(filePathStr,inZipPath) 从文件全路径中获取文件路径
    /**
     * 从文件全路径中获取文件路径
     * @author 刘虻
     * 2006-9-13下午02:54:15
     * @param filePathStr 文件全路径
     * @param inZipPath 如果路径是zip路径，即： /zip/a.jar!/com/    ture返回zip内部路径，false，返回压缩包文件路径
     * @return 文件路径
     */
    public static String getFilePath(String filePathStr,boolean inZipPath) {
        //导入参数合法化
        if (filePathStr == null 
                || filePathStr.length()==0) {
            return "";
        }
        //整理路径
        filePathStr = BaseUtil.swapString(filePathStr,"\\","/");
        int lastPoint;
        if(!inZipPath && isZipUrl(filePathStr)) {
            lastPoint = filePathStr.indexOf("!");
            if(lastPoint>0) {
                filePathStr = filePathStr.substring(0,lastPoint);
            }
        }
        if(filePathStr.startsWith("file:/")) {
            filePathStr = filePathStr.substring(6);
        }else if(filePathStr.startsWith("zip:/")) {
            filePathStr = filePathStr.substring(5);
        }
        /*
         * 如果要检测的文件没有扩展名就热闹了
         * 看起来就像文件夹名，如果文件夹名中间带个点，也挺热闹
         */
        File checkFile = new File(filePathStr);
        if(checkFile.isDirectory()) {
            return filePathStr;
        }
        lastPoint = filePathStr.lastIndexOf("/"); //最后的位置
        if (lastPoint>0) {
            return filePathStr.substring(0,lastPoint+1);
        }
        return "/";
    }
    //#endregion
    
    //#region getJarPath() 获取当前jar包所在路径
    /**
     * 获取当前jar包所在路径
     * @return 当前jar包所在路径
     * 2024年8月11日
     * @author MBG
     */
    public static String getJarPath() {
        //获取当前类URL
        URL url = (SFilesUtil.class.getProtectionDomain()).getCodeSource().getLocation();
        // 构建返回值
        String rePath = (new File(url.getPath())).getParentFile().getPath();
        if(rePath.startsWith("file:/")) {
        	rePath = rePath.substring(5);
        }
        int point = rePath.indexOf("!");
        if(point>0) {
        	rePath = rePath.substring(0,point);
        }
        // 构建返回值文件对象
        File rePathFile = new File(rePath);
        if(rePathFile.isFile()) {
        	return rePathFile.getParentFile().getPath();
        }
        return rePath;
    }
    //#endregion
    
    //#region getJarName() 获取当前jar包文件名
    /**
     * 获取当前jar包文件名
     * @return 当前jar包文件名
     * 2024年8月11日
     * @author MBG
     */
    public static String getJarName() {
        //获取当前类URL
        URL url = (SFilesUtil.class.getProtectionDomain()).getCodeSource().getLocation();
        return (new File(url.getPath())).getName();
    }
    //#endregion
    
    //#region getBaseClassPath(inCls) 获取类的根路径
    /**
     * 获取类的根路径
     * 
     * 如果这个FilesUtil类在Jar包中
     * 并且Servlet容器将这个Jar包复制到缓存文件夹中运行
     * 则无法根据自身定位到类的根路径
     * 
     * 所以需要指定一个在根路径中的类
     * 
     * @author 刘虻
     * 2009-7-17上午09:43:01
     * @param inCls 在根路径下的类
     * @return
     */
    public static String getBaseClassPath(Class<?> inCls) {
        if (inCls==null) {
            inCls = SFilesUtil.class;
        }
        //获取当前类URL
        URL url = (inCls.getProtectionDomain()).getCodeSource().getLocation();
        String baseClassPath = url.getPath();
        //获取路径
        try {
            //从Class中获取到的路径都是UTF-8编码格式的
            baseClassPath = URLDecoder.decode(baseClassPath,"UTF-8");
            baseClassPath = getFilePath(baseClassPath);
        }catch(Exception e) {}
        return baseClassPath;
    }
    //#endregion
    
    //#region getBaseClassesPath() 获取类根路径（WEB-INF/classes） 
    /**
     * 获取类根路径（WEB-INF/classes） 
     * @deprecated
     * 
     * 这个路径不再可靠了,因为weblogic这个操蛋玩意将这个文件夹在部署时迁移走了
     * 
     * @return 类根路径
     * 2016年3月31日
     * @author 马宝刚
     */
    public static String getBaseClassesPath() {
        return  getAllFilePath("../classes",getFilePath(getBaseClassPath(SFilesUtil.class),false) );
    }
    //#endregion
    
    //#region getBaseClassPath(sourceFilePath) 获取类的根路径
    /**
     * 获取类的根路径
     * 刘虻
     * 2010-2-3 下午03:03:20
     * @param sourceFilePath 在类根路径下的资源文件相对路径
     * @return 类的根路径
     */
    public static String getBaseClassPath(String sourceFilePath) {
        if(sourceFilePath==null) {
            return "";
        }
        sourceFilePath = BaseUtil.swapString(sourceFilePath,"\\","/");
        if(sourceFilePath.startsWith("/")) {
            sourceFilePath = sourceFilePath.substring(1);
        }
        //获取绝对路径
        String bPath = null;
        //绝对路径URL
        URL pathUrl = SFilesUtil.class.getClassLoader().getResource(sourceFilePath);
        int point = sourceFilePath.indexOf("/"); //文件夹分割
        
        //如果手工指定源文件的相对路径存在错误，就尝试去掉相对路径的上一级文件夹
        while(pathUrl==null && point>0) {
            sourceFilePath = sourceFilePath.substring(point+1);
            point = sourceFilePath.indexOf("/");
            pathUrl = SFilesUtil.class.getClassLoader().getResource(sourceFilePath);
        }
        if(pathUrl!=null) {
            bPath = pathUrl.getPath();
        }
        if(bPath==null) {
            return "";
        }
        sourceFilePath = bPath.substring(0,bPath.length()-sourceFilePath.length());
        //获取路径
        try {
            //从Class中获取到的路径都是UTF-8编码格式的
            sourceFilePath = URLDecoder.decode(sourceFilePath,"UTF-8");
        }catch(Exception e) {}
        if(sourceFilePath!=null) {
            //判断是否在jar包内部，如果在内部，则返回jar包所在的文件夹
            point = sourceFilePath.indexOf("!");
            if(point>0) {
                sourceFilePath = sourceFilePath.substring(0,point);
                point = sourceFilePath.lastIndexOf("/");
                if(point>0) {
                    sourceFilePath = sourceFilePath.substring(0,point);
                }
            }
        }
        if(sourceFilePath.startsWith("file:/")) {
            sourceFilePath = sourceFilePath.substring(6);
        }else if(sourceFilePath.startsWith("zip:/")) {
            sourceFilePath = sourceFilePath.substring(5);
        }
        return sourceFilePath;
    }
    //#endregion
    
    //#region isFileExist(filePath,basePath) 判断文件是否存在
    /**
     * 判断文件是否存在
     * @author 刘虻
     * 2008-7-27下午07:31:02
     * @param filePath 文件路径
     * @param basePath 根路径
     * @return 文件是否存在
     */
    public static boolean isFileExist(String filePath,String basePath) {
        if (filePath==null) {
            return false;
        }
        filePath = getAllFilePath(filePath,basePath);
        if (isZipUrl(filePath)) {
            return isZipFileExist(filePath);
        }else {
            return (new File(filePath)).exists();
        }
    }
    //#endregion
    
    //#region getFileName(filePathStr) 从文件路径中获得文件名
    /**
     * 从文件路径中获得文件名
     * @author 刘虻
     * @param filePathStr 文件全路径
     * @return 文件名
     * 2006-4-1  14:30:55
     */
    public static String getFileName(String filePathStr) {
        if(filePathStr==null || filePathStr.length()<1) {
            return "";
        }
        //获取分隔符
        int point1 = filePathStr.lastIndexOf("/");
        int point2 = filePathStr.lastIndexOf("\\");
        
        if(point1>point2) {
            return filePathStr.substring(point1+1);
        }else if(point1<point2) {
            return filePathStr.substring(point2+1);
        }
        //point1==point2 这种情况只有  都为-1时
        return filePathStr;
    }
    //#endregion
    
    //#region getPathLastName(path) 获取路径中的最后一个名称
    /**
     * 获取路径中的最后一个名称
     * 
     * 比如：  c:/a/b/c/   返回c
     *        c:/a/b/c    返回c
     *        C:/a/b/c/d.txt 返回d.txt
     * 
     * @author 刘虻
     * 2007-1-6下午12:26:41
     * @param path 路径
     * @return 最后一个名称
     */
    public static String getPathLastName(String path) {
        try {
            String filePath = 
                BaseUtil.swapString(path,"\\","/");
            String[] fileNames = BaseUtil.split(filePath,"/");
            if (fileNames==null) {
                return "";
            }
            for (int i=fileNames.length-1;i>-1;i--) {
                if (fileNames[i]!=null 
                        && fileNames[i].trim().length()>0) {
                    return fileNames[i];
                }
            }
        }catch(Exception e) {
            e.printStackTrace();
        }
        return "";
    }
    //#endregion
    
    //#region createFile(file) 根据文件路径建立文件 如果目标路径不存在，则自动建立
    /**
     * 根据文件路径建立文件 如果目标路径不存在，则自动建立
     * @author 刘虻
     * @param file 文件对象
     * @return 建立好的文件对象
     * 2006-7-27  15:30:14
     */
    public static void createFile(File file) {
        if (file==null) {
            return;
        }
        if (!file.exists()) {
            //路径
            String filePath = file.getPath();
            //建立文件夹
            (new File(getFilePath(filePath,true))).mkdirs(); 
            if(getFileName(filePath).length()>0) {
                try {
                	file.createNewFile(); //建立文件
                }catch(Exception e) {
                    e.printStackTrace();
                    System.err.println(
                            "Create File Error FilePath:["+
                            filePath+"] Error:"+BaseUtil.getExceptionMessage(e));
                }
            }
        }
    }
    //#endregion
    
    //#region createFile(filePath) 根据文件路径建立文件 如果目标路径不存在，则自动建立
    /**
     * 根据文件路径建立文件 如果目标路径不存在，则自动建立
     * @author 刘虻
     * @param filePath 文件全路径
     * @return 建立好的文件对象
     * 2006-7-27  15:30:14
     */
    public static File createFile(String filePath) {
        if (filePath==null) {
            return null;
        }
        File reFile = new File(filePath); //构建返回值
        if (!reFile.exists()) {
            //建立文件夹
            (new File(getFilePath(filePath,true))).mkdirs(); 
            if(getFileName(filePath).length()>0) {
                try {
                    reFile.createNewFile(); //建立文件
                }catch(Exception e) {
                    e.printStackTrace();
                    System.err.println(
                            "Create File Error FilePath:["+
                            filePath+"] Error:"+BaseUtil.getExceptionMessage(e));
                }
            }
        }
        return reFile;
    }
    //#endregion
    
    //#region createPath(filePath,basePath,isFilePath) 创建文件夹
    /**
     * 创建文件夹
     * @param filePath     文件路径（可以是相对路径，也可以是全路径）
     * @param basePath     根路径（如果filePath是相对路径，可以用这个参数变成全路径）
     * @param isFilePath   路径中是否包含文件名
     * @return             创建后的文件夹对象或者如果isFilePath为true时，返回filePath对应的文件对象（已经为这个文件建立好文件夹）
     * 2018年6月29日
     * @author MBG
     */
    public static File createPath(String filePath,String basePath,boolean isFilePath) {
    	if(basePath!=null && basePath.length()>0) {
    		filePath = getAllFilePath(filePath,basePath);
    	}
    	String dirPath; //文件夹路径
    	if(isFilePath) {
    		dirPath = getFilePath(filePath);
    	}else {
    		dirPath = filePath;
    	}
        File dirFile = new File(dirPath); //构建返回值
        if (!dirFile.exists()) {
            //建立文件夹
        	dirFile.mkdirs(); 
        }
        if(isFilePath) {
        	return new File(filePath); //返回文件的对象
        }
        return dirFile; 
    }
    //#endregion
    
    //#region getPathFromAllPath(allPathStr) 将全路径（包含文件名的路径）中分离出路径字符串（末尾包含/）
    /**
     * 将全路径（包含文件名的路径）中分离出路径字符串（末尾包含/）
     * @author 刘虻
     * @param allPathStr 全路径（包含文件名的路径）
     * @return 路径字符串（末尾不包含/）
     * 2006-3-28  17:58:10
     */
    public static String getPathFromAllPath(String allPathStr) {
        //导入参数合法化
        //获取末尾分隔符
        int point = allPathStr.lastIndexOf("/");
        if (point>-1) {
            return allPathStr.substring(0,point);
        }
        return allPathStr;
    }
    //#endregion
    
    //#region deletePath(path) 删除指定路径
    /**
     * 删除指定路径
     * @author 刘虻
     * 2007-1-6下午03:30:17
     * @param path 指定路径
     * @exception Exception 执行发生异常
     */
    public static void deletePath(String path) throws Exception  {
        //判断是否为文件
        File file = new File(path);
        if (!file.isFile()) {
            //构造文件夹中所有文件列表
            ArrayList<String> fileArrl = new ArrayList<String>();
            //执行搜索
            getFileList(fileArrl,path,null,"*",true,false);
            
            //保存全部路径
            SortVector<String> sv = new SortVector<String>();
            
            //删除所有文件
            File delFile; //将要删除的文件
            for (String filePath:fileArrl) {
                delFile = new File(filePath);
                if (delFile.isDirectory()) {
                    sv.add(filePath,filePath.length());
                }else {
                	delFile.delete();
                }
            }
            sv.desc();
            while(sv.hasNext()) {
            	//由内到外删除文件夹
            	(new File(sv.nextValue())).delete();
            }
        }
        file.delete();
    }
    //#endregion
    
    //#region getFileBeforeName(filePathStr) 从文件路径中获取文件前名(不包括扩展名)
    /**
     * 从文件路径中获取文件前名(不包括扩展名)
     * @author 刘虻
     * @param filePathStr 文件路径
     * @return 文件前名(不包括扩展名)
     * 2006-4-1  14:33:41
     */
    public static String getFileBeforeName(String filePathStr) {
        //获取文件名
        String fileName = getFileName(filePathStr);
        //获取扩展名分隔符
        int point  = fileName.lastIndexOf(".");
        if (point<0) {
            return fileName;
        }
        return fileName.substring(0,point);
    }
    //#endregion
    
    //#region getFileExtName(filePathStr) 获取文件路径中,文件的扩展名
    /**
     * 获取文件路径中,文件的扩展名
     * @author 刘虻
     * 2008-1-18下午12:32:38
     * @param filePathStr 文件路径
     * @return 文件的扩展名
     */
    public static String getFileExtName(String filePathStr) {
        //获取文件名
        String fileName = getFileName(filePathStr);
        //获取扩展名分隔符
        int point  = fileName.lastIndexOf(".");
        
        if (point<0) {
            return "";
        }
        return fileName.substring(point+1);
    }
    //#endregion
    
    //#region getSubPath(subPath,basePath) 相对路径中包含上一级文件夹通配符，返回去掉通配符的相对路径
    /**
     * 相对路径中包含上一级文件夹通配符，返回去掉通配符的相对路径
     * @param subPath 带通配符的相对路径
     * @param basePath 根路径
     * @return 去掉通配符的根路径
     * 2015年1月19日
     * @author 马宝刚
     */
    public static String getSubPath(String subPath,String basePath) {
        //整理根路径
        basePath = getAllFilePath(null,basePath);
        //全路径
        String allPath = getAllFilePath(subPath,basePath);
        if(allPath.length()>basePath.length()) {
            return allPath.substring(basePath.length());
        }
        return subPath; //处理失败
    }
    //#endregion
    
    //#region closeZipFile(zipFileObj) 关闭压缩文件对象
    /**
     * 关闭压缩文件对象
     * @param zipFileObj 压缩文件对象
     * 2015年7月16日
     * @author 马宝刚
     */
    public static void closeZipFile(Object zipFileObj) {
        try {
            ((ZipFile)zipFileObj).close();
        }catch(Exception e) {}
    }
    //#endregion
    
    //#region getFileContentByPath(filePath,encode) 通过文件路径获取文件内容
    /**
     * 通过文件路径获取文件内容
     * @author 刘虻
     * @param filePath 文件全路径
     * @return 文件内容
     * @throws Exception 执行发生异常
     * 2007-5-12  下午04:25:31
     */
    public static String getFileContentByPath(
                    String filePath,String encode) throws Exception {
        
        if (filePath==null 
                || filePath.length()==0) {
            return "";
        }
        if (encode==null) {
            encode = "UTF-8";
        }
        //输出流
        ByteArrayOutputStream baos = null;
        //读取缓存
        byte[] bytes = new byte[10240];
        //读取流
        InputStream fis = null;
        ZipFile zf = null; //压缩文件类
        try {
            if(isZipUrl(filePath)) {
                zf = getZipFileByUrlString(filePath);
                fis = getFileInputStreamByUrl(filePath,null);
            }else {
                fis = getFileInputStreamByUrl(filePath,null);
            }
            baos = new ByteArrayOutputStream();
            //实际读取字节数
            int readCount = fis.read(bytes);
            while (readCount > 0) {
                baos.write(bytes,0,readCount);
                readCount = fis.read(bytes); //继续读取
            }
            baos.flush(); //刷新
            return baos.toString(encode);
        } catch (IOException e) {
            e.printStackTrace();
            throw new Exception(e);
        }finally {
            try {
                fis.close();
            }catch(Exception e) {}
            if(zf!=null) {
                try {
                    zf.close();
                }catch(Exception e) {}
            }
        }
    }
    //#endregion
    
    //#region doSetFilePath(sv,basePath,subPath,fileHead,extNameList,returnSubPath,checkDisabledFile) 获得指定路径下，符合文件类型的所有文件路径 （不递归搜索子文件夹中的文件）
    /**
     * 获得指定路径下，符合文件类型的所有文件路径 （不递归搜索子文件夹中的文件）
     * @author 刘虻
     * @param sv 存放搜索后的文件路径（可以排序）
     * @param basePath 指定根文件夹路径（末尾不加/)
     * @param fileHead 搜索符合文件头名的文件（不分大小写）
     * @param extNameList 文件扩展名 为空或者*则搜索所有文件  为/则只搜索文件夹名
     * @param returnSubPath 是否返回相对路径
     * @param checkDisabledFile true如果当前文件夹中包含名为 .disabled 文件，则不扫描当前文件夹中的所有文件（包含子文件夹）
     * Exception 获得文件路径失败
     * 2006-4-1  11:26:00
     */
    private static void doSetFilePath (
            SortVector<String> sv
            ,String basePath
            ,String subPath
            ,String fileHead
            ,List<String> extNameList
            ,boolean returnSubPath
            ,boolean checkDisabledFile) throws Exception {
        if(fileHead==null) {
            fileHead = "";
        }else {
            fileHead = fileHead.toLowerCase();
        }
        final String checkFileHead = fileHead; //过滤文件名头
        //校验扩展名序列
        final ArrayList<String> checkNameArrayList = new ArrayList<String>();
        if(extNameList!=null) {
            //转换为小写
            for(Object element:extNameList) {
                if(element==null) {
                    continue;
                }
                checkNameArrayList.add(((String)element).toLowerCase());
            }
        }
        subPath = BaseUtil.swapString(subPath,"\\","/");
        basePath = BaseUtil.swapString(basePath,"\\","/");
        if(basePath.endsWith("/")) {
        	basePath = basePath.substring(0,basePath.length()-1);
        }
        //构建根路径对象
        File file = new File(basePath+"/"+subPath);
        //获得当前路径下，所有符合条件的文件和文件夹
        String[] filePathArrayListStrs = 
            file.list(new FilenameFilter() {
            //文件筛选类
            @Override
            public boolean accept(File dirPathFile, String lastNameStr) {
                lastNameStr = lastNameStr.toLowerCase(); //转换为小写
                //构建查询到的文件或路径
                File file = null;
                    try{
                        if (lastNameStr==null || "/".equals(lastNameStr)) {
                            file = new File(dirPathFile.getPath());
                        }else {
                            file = new File(dirPathFile.getPath()+"/"+lastNameStr);
                        }
                    }catch(Exception e) {
                        e.printStackTrace();
                    }
                if (file != null && file.isDirectory()) {
                    //判断是否为文件夹
                    return checkFileHead.length() <= 0;
                }else {
                    if(!lastNameStr.startsWith(checkFileHead)) {
                        return false;
                    }
                    //扩展名分割点
                    int point = lastNameStr.lastIndexOf(".");
                    String extName = ""; //扩展名
                    if(point>-1) {
                        extName = lastNameStr.substring(point+1);
                    }
                    //是否为指定扩展名的文件
                    return checkNameArrayList.size() < 1
                            || checkNameArrayList.contains("*")
                            || checkNameArrayList.contains(extName);
                }
            }
        });
        //判断是否存在文件或文件夹
        if (filePathArrayListStrs != null && filePathArrayListStrs.length >0) {
            //循环筛选
            String bPath = basePath; //处理过的根路径
            if (bPath.endsWith("/")) {
                bPath = bPath.substring(0,bPath.length()-1);
            }
            String findFilePath; //查询到的文件或路径
            for ( int i=0; i< filePathArrayListStrs.length; i++){
                if(subPath.length()>0) {
                    findFilePath = subPath+"/"+filePathArrayListStrs[i];
                }else {
                    findFilePath = filePathArrayListStrs[i];
                }
                //是否忽略当前文件夹中的文件
                if(checkDisabledFile) {
                	if((new File(basePath+"/"+findFilePath+"/.disabled")).exists()) {
                		continue;
                	}
                }
                //加入文件路径
                if(returnSubPath) {
                    sv.add(findFilePath);
                }else {
                    sv.add(basePath+"/"+findFilePath);
                }
            }
        }
    }
    //#endregion
    
    //#region doSetFilePath(filePathList,basePath,subPath,fileHead,extNameList,incChild,returnSubPath,checkDisabledFile) 获得指定路径下，符合文件类型的所有文件路径
    /**
     * 获得指定路径下，符合文件类型的所有文件路径
     * @author 刘虻
     * @param filePathList 存放搜索后的文件路径
     * @param basePath 指定根文件夹路径（末尾不加/)
     * @param fileHead 搜索符合文件头名的文件（不分大小写）
     * @param extNameList 文件扩展名 为空或者*则搜索所有文件  为/则只搜索文件夹名  注意：扩展名不带分隔符"."
     * @param incChild 是否包含子路径
     * @param returnSubPath 是否返回相对路径
     * @param checkDisabledFile true如果当前文件夹中包含名为 .disabled 文件，则不扫描当前文件夹中的所有文件（包含子文件夹）
     * Exception 获得文件路径失败
     * 2006-4-1  11:26:00
     */
    private static void doSetFilePath (
            List<String> filePathList
            ,String basePath
            ,String subPath
            ,String fileHead
            ,List<String> extNameList
            ,boolean incChild
            ,boolean returnSubPath
            ,boolean checkDisabledFile) throws Exception {
    	if(fileHead==null) {
    		fileHead = "";
    	}else {
    		fileHead = fileHead.toLowerCase();
    	}
    	final String checkFileHead = fileHead; //过滤文件名头
        //校验扩展名序列
        final ArrayList<String> checkNameArrayList = new ArrayList<String>();
        if(extNameList!=null) {
        	for(String extName:extNameList) {
        		checkNameArrayList.add(extName.toLowerCase());
        	}
        }
        subPath = BaseUtil.swapString(subPath,"\\","/");
        basePath = BaseUtil.swapString(basePath,"\\","/");
        if(basePath.endsWith("/")) {
        	basePath = basePath.substring(0,basePath.length()-1);
        }
        //是否忽略当前文件夹中的文件
        if(checkDisabledFile) {
        	if((new File(basePath+"/"+subPath+"/.disabled")).exists()) {
        		return;
        	}
        }
        //构建根路径对象
        File file = new File(basePath+"/"+subPath);
        //获得当前路径下，所有符合条件的文件和文件夹
        String[] filePathArrayListStrs = 
            file.list(new FilenameFilter() {
            //文件筛选类
            @Override
            public boolean accept(File dirPathFile, String lastNameStr) {
                lastNameStr = lastNameStr.toLowerCase(); //转换为小写
                //构建查询到的文件或路径
                File file = null;
                    try{
                        if (lastNameStr==null || "/".equals(lastNameStr)) {
                            file = new File(dirPathFile.getPath());
                        }else {
                            file = new File(dirPathFile.getPath()+"/"+lastNameStr);
                        }
                    }catch(Exception e) {
                        e.printStackTrace();
                    }
                if (file != null && file.isDirectory()) {
                    //判断是否为文件夹
                    return checkFileHead.length() <= 0;
                }else {
                	if(!lastNameStr.startsWith(checkFileHead)) {
                		return false;
                	}
                    //扩展名分割点
                    int point = lastNameStr.lastIndexOf(".");
                    String extName = ""; //扩展名
                    if(point>-1) {
                        extName = lastNameStr.substring(point+1);
                    }
                    //是否为指定扩展名的文件
                    return checkNameArrayList.size() < 1
                            || checkNameArrayList.contains("*")
                            || checkNameArrayList.contains(extName.toLowerCase());
                }
            }
        });
        //判断是否存在文件或文件夹
        if (filePathArrayListStrs != null && filePathArrayListStrs.length >0) {
            //循环筛选
            String bPath = basePath; //处理过的根路径
            if (bPath.endsWith("/")) {
                bPath = bPath.substring(0,bPath.length()-1);
            }
            String findFilePath; //查询到的文件或路径
            File findFile; //查询到的文件或文件夹
            for ( int i=0; i< filePathArrayListStrs.length; i++){
            	if(subPath.length()>0) {
            		findFilePath = subPath+"/"+filePathArrayListStrs[i];
            	}else {
            		findFilePath = filePathArrayListStrs[i];
            	}
                try {
                    findFile = getFileByName(basePath+"/"+findFilePath,null);
                }catch(Exception e) {
                    continue;
                }
                if (findFile.isDirectory() && incChild) {
                    //如果是文件夹，并且搜索子文件夹，则递归调用方法
                    doSetFilePath(filePathList,basePath,findFilePath,fileHead,checkNameArrayList,incChild,returnSubPath,checkDisabledFile);
                    if (checkNameArrayList.size()<1) {
                        continue;
                    }
                    if (checkNameArrayList.contains("*") || checkNameArrayList.contains("/")) {
                    	if(returnSubPath) {
                    		filePathList.add(findFilePath);
                    	}else {
                    		filePathList.add(basePath+"/"+findFilePath);
                    	}
                    }
                }else {
                	if(!findFilePath.endsWith(".disabled")) {
                        //加入文件路径
                    	if(returnSubPath) {
                    		filePathList.add(findFilePath);
                    	}else {
                    		filePathList.add(basePath+"/"+findFilePath);
                    	}
                	}
                }
            }
        }
    }
    //#endregion
    
    //#region searchZipFilePath(sv,basePath,fileHead,extNameList,returnSubPath) 搜索压缩文件中的文件元素列表
    /**
     * 搜索压缩文件中的文件元素列表
     * @author 刘虻
     * 2008-7-26下午03:25:00
     * @param sv 文件元素列表序列（可以排序的）
     * @param basePath 压缩文件全路径
     * @param fileHead 搜索符合文件头名的文件（不分大小写）
     * @param extNameList 符合条件的扩展名 支持*
     * @param returnSubPath 是否返回相对路径
     */
    public static void searchZipFilePath(
            SortVector<String> sv
            ,String basePath
            ,String fileHead
            ,List<String> extNameList
            ,boolean returnSubPath) {
        if (sv==null) {
            return;
        }
        if(fileHead==null) {
            fileHead = "";
        }else {
            fileHead = fileHead.toLowerCase();
        }
        //校验扩展名序列
        final ArrayList<String> checkNameArrayList = new ArrayList<String>();
        if(extNameList!=null) {
            //转换为小写
            for(Object element:extNameList) {
                if(element==null) {
                    continue;
                }
                checkNameArrayList.add(((String)element).toLowerCase());
            }
        }
        basePath = BaseUtil.swapString(basePath,"\\","/");
        //构建压缩文件对象
        ZipFile zf = getZipFileByUrlString(basePath);
        if (zf==null) {
            return;
        }
        try {
            //获取压缩文件信息迭代
            Enumeration<? extends ZipEntry> zpEnum = zf.entries();
            if (zpEnum==null) {
                return;
            }
            //获取子文件夹
            String childPath = getZipFileChildPath(basePath);
            if (childPath.startsWith("/")) {
                childPath = childPath.substring(1);
            }
            //获取压缩包路径
            basePath = getZipFilePathByUrlString(basePath);
            ZipEntry entry = null; //文件信息元素
            while(zpEnum.hasMoreElements()) {
                //获取元素
                entry = zpEnum.nextElement();
                if (entry==null 
                        || "..\\".equals(entry.getName())
                        || entry.isDirectory()
                    ) {
                    continue;
                }
                //压缩文件元素文件路径 转换为小写，扩展名搜索不分大小写
                String filePath = entry.getName().toLowerCase();
                
                //过滤掉不符合文件头名
                if(fileHead.length()>0 && !filePath.startsWith(fileHead)) {
                    continue;
                }
                
                String lastNameStr = ""; //扩展名
                //扩展名分割点
                int point = filePath.lastIndexOf(".");
                if(point>-1) {
                    lastNameStr = filePath.substring(point+1);
                }
                //是否符合条件
                if (filePath.startsWith(childPath)) {
                    if (checkNameArrayList.size()<1
                        || checkNameArrayList.contains("*")
                        || checkNameArrayList.contains(lastNameStr)) {
                        //这里不能将子文件路径转换成小写，否则通过小写的子路径不一定能获取到
                        //对应的压缩文件元素
                        if(returnSubPath) {
                            sv.add(entry.getName());
                        }else {
                            sv.add("zip:"+basePath+"!/"+entry.getName());
                        }
                    }
                }
            }
        }finally {
            try {
                zf.close();
            }catch(Exception e) {}
        }
    }
    //#endregion

    //#region searchZipFile(filePathList,basePathStr,fileHead,extName,returnSubPath) 搜索压缩文件中的文件元素列表
    /**
     * 搜索压缩文件中的文件元素列表
     * @author 刘虻
     * 2008-7-26下午03:25:00
     * @param filePathList 文件元素列表序列
     * @param basePathStr 压缩文件全路径
     * @param fileHead 搜索符合文件头名的文件（不分大小写）
     * @param extName 符合条件的扩展名 支持，多个用分号分割
     * @param returnSubPath 是否返回相对路径
     */
    public static void searchZipFile (
            List<String> filePathList
            ,String basePathStr
            ,String fileHead
            ,String extName
            ,boolean returnSubPath) {
    	if(extName==null || extName.length()<1) {
    		searchZipFilePath(filePathList,basePathStr,fileHead, null,returnSubPath);
    		return;
    	}
    	searchZipFilePath(filePathList,basePathStr,fileHead,BaseUtil.splitToList(extName,";"),returnSubPath);
    }
    //#endregion
    
    //#region searchZipFilePath(filePathList,basePathStr,fileHead,extNameList,returnSubPath) 搜索压缩文件中的文件元素列表
    /**
     * 搜索压缩文件中的文件元素列表
     * @author 刘虻
     * 2008-7-26下午03:25:00
     * @param filePathList 文件元素列表序列
     * @param basePathStr 压缩文件全路径
     * @param fileHead 搜索符合文件头名的文件（不分大小写）
     * @param extNameList 符合条件的扩展名 支持*
     * @param returnSubPath 是否返回相对路径
     */
    public static void searchZipFilePath (
            List<String> filePathList
            ,String basePathStr
            ,String fileHead
            ,List<String> extNameList
            ,boolean returnSubPath) {
        if (filePathList==null) {
            return;
        }
        if(fileHead==null) {
        	fileHead = "";
        }else {
        	fileHead = fileHead.toLowerCase();
        }
        //校验扩展名序列
        final ArrayList<String> checkNameArrayList = new ArrayList<String>();
        if(extNameList!=null) {
            //转换为小写
            for(Object element:extNameList) {
                if(element==null) {
                    continue;
                }
                checkNameArrayList.add(((String)element).toLowerCase());
            }
        }
        basePathStr = BaseUtil.swapString(basePathStr,"\\","/");
        //构建压缩文件对象
        ZipFile zf = getZipFileByUrlString(basePathStr);
        if (zf==null) {
            return;
        }
        try {
            //获取压缩文件信息迭代
            Enumeration<? extends ZipEntry> zpEnum = zf.entries();
            if (zpEnum==null) {
                return;
            }
            //获取子文件夹
            String childPath = getZipFileChildPath(basePathStr);
            if (childPath.startsWith("/")) {
                childPath = childPath.substring(1);
            }
            //获取压缩包路径
            basePathStr = getZipFilePathByUrlString(basePathStr);
            ZipEntry entry = null; //文件信息元素
            while(zpEnum.hasMoreElements()) {
                //获取元素
                entry = zpEnum.nextElement();
                if (entry==null 
                        || "..\\".equals(entry.getName())
                        || entry.isDirectory()
                    ) {
                    continue;
                }
                //压缩文件元素文件路径 转换为小写，扩展名搜索不分大小写
                String filePath = entry.getName().toLowerCase();
                
                //过滤掉不符合文件头名
                if(fileHead.length()>0 && !filePath.startsWith(fileHead)) {
                	continue;
                }
                
                String lastNameStr = ""; //扩展名
                //扩展名分割点
                int point = filePath.lastIndexOf(".");
                if(point>-1) {
                    lastNameStr = filePath.substring(point+1);
                }
                //是否符合条件
                if (filePath.startsWith(childPath)) {
                    if (checkNameArrayList.size()<1
                        || checkNameArrayList.contains("*")
                        || checkNameArrayList.contains(lastNameStr)) {
                        //这里不能将子文件路径转换成小写，否则通过小写的子路径不一定能获取到
                        //对应的压缩文件元素
                    	if(returnSubPath) {
                    		filePathList.add(entry.getName());
                    	}else {
                    		filePathList.add("zip:"+basePathStr+"!/"+entry.getName());
                    	}
                    }
                }
            }
        }finally {
            try {
                zf.close();
            }catch(Exception e2) {}
        }
    }
    //#endregion
    
    //#region searchZipFilePath(filePathList,fileHead,extNameList,zf) 搜索压缩文件中的文件元素列表
    /**
     * 搜索压缩文件中的文件元素列表
     * @author 刘虻
     * 2008-7-26下午03:25:00
     * @param filePathList 文件元素列表序列
     * @param fileHead 搜索符合文件头名的文件（不分大小写）
     * @param extNameList 符合条件的扩展名 支持*
     * @param zf          压缩文件对象
     */
    public static void searchZipFilePath (
            List<String> filePathList
            ,String fileHead
            ,List<String> extNameList
            ,ZipFile zf) {
        if (zf==null || filePathList==null) {
            return;
        }
        if(fileHead==null) {
        	fileHead = "";
        }else {
        	fileHead = fileHead.toLowerCase();
        }
        //校验扩展名序列
        final ArrayList<String> checkNameArrayList = new ArrayList<String>();
        if(extNameList!=null) {
            //转换为小写
            for(Object element:extNameList) {
                if(element==null) {
                    continue;
                }
                checkNameArrayList.add(((String)element).toLowerCase());
            }
        }
        try {
            //获取压缩文件信息迭代
            Enumeration<? extends ZipEntry> zpEnum = zf.entries();
            if (zpEnum==null) {
                return;
            }
            ZipEntry entry = null; //文件信息元素
            while(zpEnum.hasMoreElements()) {
                //获取元素
                entry = zpEnum.nextElement();
                if (entry==null 
                        || "..\\".equals(entry.getName())
                        || entry.isDirectory()
                    ) {
                    continue;
                }
                //压缩文件元素文件路径 转换为小写，扩展名搜索不分大小写
                String filePath = entry.getName().toLowerCase();
                
                //过滤掉不符合文件头名
                if(fileHead.length()>0 && !filePath.startsWith(fileHead)) {
                	continue;
                }
                String lastNameStr = ""; //扩展名
                //扩展名分割点
                int point = filePath.lastIndexOf(".");
                if(point>-1) {
                    lastNameStr = filePath.substring(point+1);
                }
                if (checkNameArrayList.size()<1
                    || checkNameArrayList.contains("*")
                    || checkNameArrayList.contains(lastNameStr)) {
                    //这里不能将子文件路径转换成小写，否则通过小写的子路径不一定能获取到
                    //对应的压缩文件元素
                	filePathList.add(entry.getName());
                }
            }
        }finally {
        	//zf这个类实例是传入进来的，所以不能在这里关闭，后续还得用
            //try {
            //    zf.close();
            //}catch(Exception e2) {}
        }
    }
    //#endregion
    
    //#region getDriveArrayList() 获取系统有效驱动器序列
    /**
     * 获取系统有效驱动器序列
     * @author 刘虻
     * 2008-8-19下午09:41:44
     * @return 系统有效驱动器序列
     */
    public static ArrayList<String> getDriveArrayList() {
        //构造返回值
        ArrayList<String> reArrayList = new ArrayList<String>();
        String checkDrive = null; //检测盘符
        File checkFile = null; //检测文件
        for(int i=65;i<91;i++) {
            checkDrive = (char) i +":";
            checkFile = new File(checkDrive);
            if (checkFile.exists()) {
                reArrayList.add(checkDrive);
            }
        }
        return reArrayList;
    }
    //#endregion
    
    //#region fixUpPath(dummyPath,nativeCall) 处理路径中的通配符
    /**
     * 处理路径中的通配符
     * @author 刘虻
     * 2009-11-10下午03:09:56
     * @param dummyPath 待处理路径
     * @return 处理后的路径
     */
    private static String fixUpPath(String dummyPath,boolean nativeCall) {
        StringBuilder rePathSbf = new StringBuilder(); // 构造返回路径
        String        sep       = File.separator;      // 路径分隔符
        
        if(!nativeCall) {
        	dummyPath = fixPathByOs(dummyPath);
        }
        
        if (dummyPath.startsWith("/")) {
            rePathSbf.append("/");
            dummyPath = dummyPath.substring(1);
            sep = "/";
        }else if (dummyPath.indexOf("://")>0) {
        	rePathSbf.append(dummyPath.substring(0,dummyPath.indexOf("://")+3));
        	dummyPath = dummyPath.substring(dummyPath.indexOf("://")+3);
        	sep = "/";
        }else if (dummyPath.indexOf(":\\")==1) {
            rePathSbf.append(dummyPath, 0, 3);
            dummyPath = dummyPath.substring(3);
            sep = "\\";
        }else if (dummyPath.indexOf(":\\")==2) {
            rePathSbf.append(dummyPath, 0, 4);
            dummyPath = dummyPath.substring(4);
            sep = "\\";
        }
        //分割成文件夹名
        List<String> pathSubArrayList = BaseUtil.splitToList(dummyPath,sep);
        //整理后的文件夹序列
        ArrayList<String> fixArrayList = new ArrayList<String>();
        boolean isWar = false; //是否为war
        if (pathSubArrayList!=null) {
            for (String thisPath:pathSubArrayList) {
                //endsWith("!") 是因为如果部署成war格式 war文件名与展开时的根路径名不同
                if ("..".equals(thisPath)) {
                    if (fixArrayList.size()>0 ) {
                        if (SString.valueOf(fixArrayList.get(fixArrayList.size()-1)).endsWith("!")) {
                            isWar = true;
                        }else {
                            fixArrayList.remove(fixArrayList.size()-1);
                        }
                    }
                }else if(".".equals(thisPath)) {
                    continue;
                }else {
                    if (isWar) {
                        isWar = false;
                    }else {
                        fixArrayList.add(thisPath);
                    }
                }
            }
        }
        for (int i=0;i<fixArrayList.size();i++) {
            if (i>0) {
                rePathSbf.append(sep);
            }
            rePathSbf.append(fixArrayList.get(i));
        }
        return rePathSbf.toString();
    }
    //#endregion
    
    //#region fixUpPath(dummyPath) 处理路径中的通配符
    /**
     * 处理路径中的通配符
     * @author 刘虻
     * 2009-11-10下午03:09:56
     * @param dummyPath 待处理路径
     * @return 处理后的路径
     */
    public static String fixUpPath(String dummyPath) {
    	return fixUpPath(dummyPath,false);
    }
    //#endregion
    
    //#region getFristPath(path) 获取第一个文件夹
    /**
     * 获取第一个文件夹
     * @author 刘虻
     * 2008-1-30下午08:31:01
     * @param path 全路径
     * @return 第一个文件夹
     */
    protected static String getFristPath(String path) {
        while(path.startsWith(File.separator)) {
            path = path.substring(1);
        }
        //文件夹分割点
        int point = path.indexOf(File.separator);
        if (point>-1) {
            return path.substring(0,point);
        }
        return "";
    }
    //#endregion
    
    //#region isZipUrl(urlStr) 是否为压缩文件路径
    /**
     * 是否为压缩文件路径
     * @author 刘虻
     * 2008-7-26下午04:08:14
     * @param urlStr 测试路径
     * @return 是否为压缩文件路径
     */
    public static boolean isZipUrl(String urlStr) {
        if (urlStr==null) {
            return false;
        }
        urlStr = urlStr.toLowerCase();
        if (urlStr.startsWith("zip:")) {
            return true;
        }
        return urlStr.indexOf(".jar!/") > -1 || urlStr.indexOf(".zip!/") > -1;
    }
    //#endregion
    
    //#region getZipFileContent(subPath,zipFile,enc) 读取压缩文件内容
    /**
     * 读取压缩文件内容
     * @param subPath 压缩文件相对路径
     * @param zipFile 压缩文件全路径
     * @param enc 文件内容编码
     * @return 文件内容
     * 2016年7月19日
     * @author MBG
     */
    public static String getZipFileContent(String subPath,String zipFile,String enc) {
    	//获取压缩文件对象
    	ZipFile zip = getZipFileByUrlString(zipFile);
    	if(zip==null) {
    		return "";
    	}
    	try {
    		//读取压缩文件内容
    		InputStream is = getZipFileInputStreamByUrl(subPath,zip);
    		if(is==null) {
    			return "";
    		}
    		return FileCopyTools.copyToString(is,enc);
    	}catch(Exception e) {
    		e.printStackTrace();
    	}
    	return "";
    }
    //#endregion
    
    //#region getZipFileIsByUrl(allUrl) 通过完整的压缩文件URL，获取指定内部文件流
    /**
     * 通过完整的压缩文件URL，获取指定内部文件流
     * 完整的压缩文件URL格式：
     * jar:file:/E:/javacodes/gla/gla_ent/ent_server/WEB-INF/lib/jphenix_sdk.jar!/lib/servlet-api.jar
     * @param allUrl 压缩文件URL
     * @return 指定内部文件流
     * @throws Exception 异常
     * 2016年12月9日
     * @author MBG
     */
    @SuppressWarnings("resource")
	public static InputStream getZipFileIsByUrl(String allUrl) throws Exception {
    	if(allUrl==null) {
    		throw new Exception("The Zip File Path is Null");
    	}
    	//找到主文件与内部文件路径分隔符
    	int point = allUrl.indexOf("!");
    	if(point<0) {
    		throw new Exception("The Zip File Path["+allUrl+"] Is Wrong [e.g.  jar:file:C:/path/file.jar!/com/demo.class");
    	}
    	//主文件路径
    	String basePath = allUrl.substring(0,point);
    	//内部文件路径
    	String subPath = allUrl.substring(point+1);
    	//截取路径头部描述信息
    	point = basePath.toLowerCase().indexOf("file:");
    	if(point>-1) {
    		basePath = basePath.substring(point+5);
    	}
    	//构建压缩文件对象
    	File baseFile = new File(basePath);
    	if(!baseFile.exists() || !baseFile.isFile()) {
    		throw new Exception("The Zip File Path["+allUrl+"] Is Wrong. Not Find The File:["+basePath+"]");
    	}
    	ZipFile zipFile = new ZipFile(baseFile);
        if (subPath.startsWith("/")) {
        	subPath = subPath.substring(1);
        }
        return zipFile.getInputStream(zipFile.getEntry(subPath));
    }
    //#endregion
    
    //#region getZipFileInputStreamByUrl(childPath,zipFileObj) 获取压缩文件中指定文件读入流
    /**
     * 获取压缩文件中指定文件读入流
     * @author 刘虻
     * 2008-7-26下午05:32:45
     * @param childPath  子路径
     * @param zipFileObj 如果要读取压缩包中多个文件，需要先获取压缩包对象
     *                              ，在操作完全部子压缩文件后，需要关闭压缩包对象
     * @return 压缩文件中指定文件读入流
     * @throws Exception 执行发生异常
     */
    public static InputStream getZipFileInputStreamByUrl(String childPath,Object zipFileObj) throws Exception {
        ZipFile zf = null; //压缩包对象
        if(zipFileObj==null) {
        	throw new MsgException(SFilesUtil.class,"The FileObject Is Null ["+childPath+"]");
        }else {
            if(!(zipFileObj instanceof ZipFile)) {
                throw new MsgException(SFilesUtil.class,"The FileObject Not ZipFile Object  ["+childPath+"]");
            }
            zf = (ZipFile)zipFileObj; //压缩文件
        }
        if (childPath.startsWith("/")) {
            childPath = childPath.substring(1);
        }
        try {
            return zf.getInputStream(zf.getEntry(childPath));
        }catch(Exception e) {}
        return null;
    }
    //#endregion
    
    //#region getZipFileTime(filePath) 获取压缩文件中指定文件元素的时间
    /**
     * 获取压缩文件中指定文件元素的时间
     * @author 刘虻
     * 2008-7-27下午08:46:25
     * @param filePath 文件元素全路径 zip:c:/jar/a.class
     * @return 文件时间
     */
    public static long getZipFileTime(String filePath) {
        return getZipFileTime(
                getZipFileByUrlString(filePath)
                ,getZipFileChildPath(filePath));
    }
    //#endregion
    
    //#region isZipFileExist(filePath) 判断压缩包中是否存在该文件
    /**
     * 判断压缩包中是否存在该文件
     * @author 刘虻
     * 2008-7-27下午07:30:11
     * @param filePath 文件URL  zip:/c:/a.zip!/com/a.class
     * @return 是否存在
     */
    public static boolean isZipFileExist(String filePath) {
        return isZipInsideFileExist(getZipFileByUrlString(filePath),getZipFileChildPath(filePath));
    }
    //#endregion
    
    //#region isZipInsideFileExist(zf,childPath) 判断压缩包中是否存在该文件
    /**
     * 判断压缩包中是否存在该文件
     * @author 刘虻
     * 2008-7-27下午07:29:10
     * @param zf 压缩包路径
     * @param childPath 子路径
     * @return 是否存在
     */
    protected static boolean isZipInsideFileExist(ZipFile zf,String childPath) {
        try {
            if (childPath==null) {
                return false;
            }
            if (childPath.startsWith("/")) {
                childPath = childPath.substring(1);
            }
            return zf.getEntry(childPath) != null;
        }catch(Exception e) {}
        return false;
    }
    //#endregion
    
    //#region getZipFileChildPath(fileUrl) 获取压缩文件内部相对路径
    /**
     * 获取压缩文件内部相对路径
     * @author 刘虻
     * 2008-7-26下午05:19:07
     * @param fileUrl 压缩文件全路径
     * @return 压缩文件内部相对路径
     */
    public static String getZipFileChildPath(String fileUrl) {
        if (fileUrl==null) {
            return "/";
        }
        //获取子路径分割点
        int point = fileUrl.lastIndexOf("!");
        if (point<0) {
            return "/";
        }
        return fileUrl.substring(point+1);
    }
    //#endregion
    
    //#region getZipFile(zipFileObj) 通过文件对象获取压缩文件对象
    /**
     * 通过文件对象获取压缩文件对象
     * @param zipFileObj 文件对象
     * @return           压缩文件对象
     * 2019年1月15日
     * @author MBG
     */
    public static ZipFile getZipFile(File zipFileObj) {
    	try {
    		return new ZipFile(zipFileObj);
    	}catch(Exception e) {
    		e.printStackTrace();
    	}
    	return null;
    }
    //#endregion
    
    //#region getZipFileByUrlString(zipFileUrl) 通过URL路径获取压缩文件对象
    /**
     * 通过URL路径获取压缩文件对象
     * @author 刘虻
     * 2008-7-26下午03:11:06
     * @param zipFileUrl URL路径
     * @return 压缩文件对象
     */
    public static ZipFile getZipFileByUrlString(String zipFileUrl) {
        //获取压缩文件路径
        zipFileUrl = getZipFilePathByUrlString(zipFileUrl);
        if (zipFileUrl==null) {
            return null;
        }
        try {
            return new ZipFile(zipFileUrl);
        }catch(Exception e) {}
        return null;
    }
    //#endregion
    
    //#region getZipFilePathByUrlString(zipFileUrl) 通过压缩文件URL获取压缩文件物理路径
    /**
     * 通过压缩文件URL获取压缩文件物理路径
     * 
     *  如：zip:d:/zipfile/one.war!/com/Test.class
     * 
     *  返回 d:/zipfile/one.war
     * 
     * @author 刘虻
     * 2008-7-26下午03:13:49
     * @param zipFileUrl 压缩文件URL
     * @return 压缩文件物理路径
     */
    public static String getZipFilePathByUrlString(String zipFileUrl) {
        if (zipFileUrl==null) {
            return null;
        }
        if (zipFileUrl.startsWith("zip:")) {
            zipFileUrl = zipFileUrl.substring(4);
        }else if(zipFileUrl.startsWith("file:")) {
            zipFileUrl = zipFileUrl.substring(5);
        }
        //获取压缩文件中子路径分隔符
        int childFilePoint = zipFileUrl.lastIndexOf("!");
        if(childFilePoint>-1) {
            zipFileUrl = zipFileUrl.substring(0,childFilePoint);
        }
        return zipFileUrl;
    }
    //#endregion
    
    //#region getZipFileTime(zf,childPath) 获取压缩包中文件的时间
    /**
     * 获取压缩包中文件的时间
     * @author 刘虻
     * 2008-7-27下午08:45:07
     * @param zf 压缩文件对象
     * @param childPath 元素相对路径
     * @return 文件时间
     */
    protected static long getZipFileTime(ZipFile zf,String childPath) {
        if (zf==null || childPath==null) {
            return 0;
        }
        //获取文件元素
        ZipEntry entry = zf.getEntry(childPath);
        if (entry==null) {
            return 0;
        }
        return entry.getTime();
    }
    //#endregion
    
    //#region getZipFileByUrl(zipFileUrl) 通过URL获取压缩文件对象
    /**
     * 通过URL获取压缩文件对象
     * @author 刘虻
     * 2008-7-26下午03:12:13
     * @param zipFileUrl URL路径
     * @return 压缩文件对象
     */
    public static ZipFile getZipFileByUrl(URL zipFileUrl) {
        if (zipFileUrl==null) {
            return null;
        }
        return getZipFileByUrlString(zipFileUrl.toString());
    }
    //#endregion 
    
    //#region fixPath(path) 处理路径分隔符（正斜杠，反斜杠，重复杠，杠头开花）
    /**
     * 处理路径分隔符（正斜杠，反斜杠，重复杠，杠头开花）
     * 整理路径 将 \ 转换为 /  去掉重复的路径分隔符
     * 刘虻
     * 2011-5-5 下午01:31:29
     * @param path 未整理的路径
     * @return 整理后的路径
     */
    public static String fixPath(String path) {
        if(path.indexOf("\\")>-1) {
            path = BaseUtil.swapString(path,"\\","/");
            return fixPath(path);
        }
        if(path.indexOf("//")>-1) {
            path = BaseUtil.swapString(path,"//","/");
            return fixPath(path);
        }
        return path;
    }
    //#endregion
    
    //#region addContent(filePath,content) 将指定内容追加写入文件
    /**
     * 将指定内容追加写入文件
     * @param filePath   文件绝对路径（如果文件不存在，会自动建立）
     * @param content    写入内容字符串 (UTF-8编码）
     * @return           是否写入成功
     * 2018年5月28日
     * @author MBG
     */
    public static boolean addContent(String filePath,String content) {
    	try {
    		return putContent(filePath,content.getBytes(StandardCharsets.UTF_8),true);
    	}catch(Exception e) {
    		e.printStackTrace();
    	}
    	return false;
    }
    //#endregion
    
    //#region putContent(filePath,content,encoding,append) 将指定内容写入文件
    /**
     * 将指定内容写入文件
     * @param filePath   文件绝对路径（如果文件不存在，会自动建立）
     * @param content    写入内容字符串
     * @param encoding   写入内容编码格式
     * @param append     是否为追加写入
     * @return           是否写入成功
     * 2018年5月28日
     * @author MBG
     */
    public static boolean putContent(String filePath,String content,String encoding,boolean append) {
    	if(content==null ||content.length()<1) {
    		return false;
    	}
    	if(encoding==null || encoding.length()<1) {
    		return putContent(filePath,content.getBytes(),append);
    	}
    	try {
    		return putContent(filePath,content.getBytes(encoding),append);
    	}catch(Exception e) {
    		e.printStackTrace();
    	}
    	return false;
    }
    //#endregion
    
    //#region putContent(filePath,contents,append) 将指定内容写入文件
    /**
     * 将指定内容写入文件
     * @param filePath  文件绝对路径（如果文件不存在，会自动建立）
     * @param contents  写入内容字节数组
     * @param append    是否为追加写入
     * @return          是否写入成功
     * 2018年5月28日
     * @author MBG
     */
    public static boolean putContent(String filePath,byte[] contents,boolean append) {
    	//构建写入文件
    	File out = createFile(filePath);
		if (!out.exists()) {
			return false;
		}
		//构造输出流
		OutputStream outStream = null;
		try {
			//构建文件输出流
			outStream = new FileOutputStream(out,true);
			//写入内容
			outStream.write(contents);
			return true;
		}catch(Exception e) {
			e.printStackTrace();
		}finally {
			try {
				outStream.flush();
			}catch(Exception e2) {}
			try {
				outStream.close();
			}catch(Exception e2) {}
			outStream = null;
		}
		return false;
    }
    //#endregion
    
    //#region createZipFile(zipFilePath,basePath,pathList) 将指定文件打包到ZIP文件中
    /**
     * 将指定文件打包到ZIP文件中
     * @param zipFilePath    zip文件绝对路径
     * @param basePath       需要压缩文件的根文件夹
     * @param pathList       需要打包的文件相对路径序列
     * @return               是否打包成功
     * 2018年6月13日
     * @author MBG
     */
    public static boolean createZipFile(String zipFilePath,String basePath,List<String> pathList) {
    	return createZipFile(zipFilePath,basePath,pathList,null);
    }
    //#endregion
    
    //#region createZipFile(zipFilePath,basePath,pathList,showPathList) 将指定文件打包到ZIP文件中
    /**
     * 将指定文件打包到ZIP文件中
     * @param zipFilePath    zip文件绝对路径
     * @param basePath       需要压缩文件的根文件夹
     * @param pathList       需要打包的文件相对路径序列
     * @param showPathList   需要在压缩包中显示的文件路径序列
     *                      （由于上传后保存在文件夹中的文件名都被修改成id值，在这里需要替换为原来的文件名）
     * @return               是否打包成功
     * 2018年6月13日
     * @author MBG
     */
    public static boolean createZipFile(
    		String zipFilePath,String basePath
    	   ,List<String> pathList,List<String> showPathList) {
    	//构建压缩文件对象路径
    	File file = createFile(zipFilePath);
    	if(!file.exists()) {
    		return false;
    	}
    	ZipOutputStream zos = null; //压缩文件输出流
    	try {
    		if(pathList==null || pathList.size()<1) {
    			//返回空的压缩包
    			return true;
    		}
    		if(showPathList==null || showPathList.size()!=pathList.size()) {
    			showPathList = pathList;
    		}
    		zos = new ZipOutputStream(new FileOutputStream(file));
    		List<String> cFileList; //子文件序列
    		File cFile;             //子文件对象
    		ZipEntry czip;          //压缩文件中的文件元素
    		String filePath;        //保存的文件相对路径
    		String showFilePath;    //需要在压缩包中显示的文件路径（比如中文的文件名）
    		for(int i=0;i<pathList.size();i++) {
    			filePath     = pathList.get(i);
    			showFilePath = showPathList.get(i);
				if(filePath.length()>0 && !filePath.startsWith("/")) {
					filePath = "/"+filePath;
				}
				if(showFilePath.startsWith("/")) {
					showFilePath = showFilePath.substring(1);
				}
    			cFile = new File(basePath+filePath);
    			if(!cFile.exists()) {
    				continue;
    			}
    			
    			if(cFile.isFile()) {
    				//单个文件
    				czip = new ZipEntry(showFilePath);
    				czip.setTime(cFile.lastModified());
    				czip.setSize(cFile.length());
    				zos.putNextEntry(czip);
    				//放入文件内容
    				copyZipScream(cFile,zos);
    			}else if(cFile.isDirectory()) {
        			cFileList = new ArrayList<String>();
        			//搜索指定文件夹中的全部文件
        			getFileList(cFileList,basePath+filePath,null,null,true,true);
        			for(String ele:cFileList) {
        				cFile = new File(basePath+(filePath.length()<1?"/":filePath)+ele);
        				if(cFile.isDirectory() || !cFile.exists()) {
        					continue;
        				}
        				czip = new ZipEntry(filePath+ele);
        				czip.setTime(cFile.lastModified());
        				czip.setSize(cFile.length());
        				zos.putNextEntry(czip);
        				//放入文件内容
        				copyZipScream(cFile,zos);
        			}
    			}
    		}
    		return true;
    	}catch(Exception e) {
    		e.printStackTrace();
    	}finally {
    		try {
    			zos.flush();
    		}catch(Exception e2) {}
    		try {
    			zos.close();
    		}catch(Exception e2) {}
    		zos = null;
    	}
    	return false;
    }
    //#endregion
 
    //#region unzip(zipFilePath,exportBasePath) 执行解压缩（支持 zip,jar,war）
    /**
     * 执行解压缩（支持 zip,jar,war）
     * @param  zipFilePath     压缩文件全路径
     * @param  exportBasePath  解压到目标根路径（为空时去压缩文件所在路径）
     * @throws Exception       异常
     * 2024年6月19日
     * @author MBG
     */
    public static void unzip(String zipFilePath,String exportBasePath) throws Exception {
    	ZipFile zipf = null; // 压缩包对象
    	try {
	    	// 构建压缩包对象
	    	zipf = new ZipFile(zipFilePath);
	    	
	    	if(exportBasePath==null || exportBasePath.length()<1) {
	    		exportBasePath = (new File(zipFilePath)).getParentFile().getPath();
	    	}
	    	// 返回压缩包中的元素信息对象
	    	Enumeration<? extends ZipEntry> entries = zipf.entries();
	    	while(entries.hasMoreElements()) {
	    		writeZipFile(zipf,entries.nextElement(),exportBasePath);
	    	}
    	}finally {
    		if(zipf!=null) {
    			try {
    				zipf.close();
    			}catch(Exception e2) {}
    		}
    	}
    }
    //#endregion
    
    //#region zip(srcFilePath,targetFilePath) 将指定文件压缩到目标文件中
    /**
     * 将指定文件压缩到目标文件中
     * 注意：压缩包根路径中只有这个文件
     * @param srcFilePath     需要压缩的相对路径
     * @param targetFilePath  目标绝对路径（包含压缩文件名）
     * @return                是否压缩成功
     * 2024年6月20日
     * @author MBG
     */
    public static boolean zip(String srcFilePath,String targetFilePath) {
    	if(srcFilePath==null || srcFilePath.length()<1) {
    		return false;
    	}
    	// 构建文件路径序列
    	List<String> paths = new ArrayList<>();
    	paths.add(srcFilePath);
    	
    	// 构建监测被压缩文件
    	File checkPath = new File(srcFilePath);
    	return zip(checkPath.isDirectory()?checkPath.getPath():checkPath.getParent(),paths,targetFilePath);
    }
    //#endregion
    
    //#region zip(srcPaths,targetFilePath) 将指定多个文件压缩到目标文件中
    /**
     * 将指定多个文件压缩到目标文件中
     * @param srcBasePath     需要压缩的文件所在根文件夹
     * @param srcPaths        需要压缩的相对路径序列
     * @param targetFilePath  目标绝对路径（包含压缩文件名）
     * @return                是否压缩成功
     * 2024年6月20日
     * @author MBG
     */
    public static boolean zip(String srcBasePath,List<String> srcPaths,String targetFilePath) {
    	
    	//#region 声明处理变量
    	ZipOutputStream       zos            = null;                     // 压缩文件写入流
    	WritableByteChannel   writeChannel   = null;                     // 压缩文件通道
    	if(srcBasePath==null) srcBasePath    = "";                       // 变量合法化
    	int                   basePathLength = srcBasePath.length()+1;   // 根路径长度
		File                  targetFile     = new File(targetFilePath); // 构造目标文件对象
		//#endregion
		
    	try {
    		//#region 如果目标压缩包所在文件夹不存在，自动创建路径
    		if(!targetFile.getParentFile().exists()) {
    			Files.createDirectories(Paths.get(targetFile.getParent()));
    		}
    		//#endregion

            zos = new ZipOutputStream(new FileOutputStream(targetFile));

            writeChannel = Channels.newChannel(zos);
            
            //#region 遍历路径执行压缩
            for(String sPath:srcPaths) {
            	doZip(zos,writeChannel,new File(sPath),basePathLength);
            }
            //#endregion
            
            zos.flush();
    	}catch(Exception e) {
    		e.printStackTrace();
    		return false;
    	}finally {
    		//#region 关闭相关对象
    		if(writeChannel!=null) {
    			try {
    				writeChannel.close();
    			}catch(Exception e2) {}
    		}
    		if(zos!=null) {
    			try {
    				zos.close();
    			}catch(Exception e2) {}
    		}
    		//#endregion
    	}
    	return true;
    }
    
    //#region doZip(zos,writeChannel,sFile,basePathLength) 执行压缩，递归函数
    /**
     * 执行压缩，递归函数
     * @param zos              压缩文件输出流
     * @param writeChannel     压缩文件通道
     * @param sFile            待压缩的文件对象
     * @param basePathLength   待压缩文件所属根路径长度（用于计算相对路径）
     * @throws Exception       异常
     * 2024年6月20日
     * @author MBG
     */
    private static void doZip(ZipOutputStream zos,WritableByteChannel writeChannel,File sFile,int basePathLength) throws Exception {
    	String          subPath;          // 需要压缩的文件的相对路径
    	String          pathStr;          // 需要压缩的文件路径
    	FileInputStream fis       = null; // 原始文件读入流
    	FileChannel     channel   = null; // 需要压缩文件的通道
    	try {
        	//#region 原路径是文件
        	if(sFile.isFile()) {
        		pathStr = sFile.getPath();
        		if(pathStr.length()>basePathLength) {
        			subPath = pathStr.substring(basePathLength);
        		}else {
        			subPath = pathStr;
        		}
        		zos.putNextEntry(new ZipEntry(subPath));
        		fis     = new FileInputStream(sFile);
                channel = fis.getChannel();
                
                channel.transferTo(0, sFile.length(), writeChannel);
                
                channel.close();
                zos.closeEntry();
        	}
        	//#endregion
        	
        	//#region 原路径是文件夹
        	else {
        		File[] files = sFile.listFiles();
        		if(files!=null && files.length>0) {
        			for(File ele:files) {
        				// 递归执行压缩
        				doZip(zos,writeChannel,ele,basePathLength);
        			}
        		}
        	}
        	//#endregion
    	}finally {
    		//#region 关闭相关对象
    		if(channel!=null) {
    			try {
    				channel.close();
    			}catch(Exception e2) {}
    		}
    		if(fis!=null) {
    			try {
    				fis.close();
    			}catch(Exception e2) {}
    		}
    		//#endregion
    	}
    }
    //#endregion
    
    //#endregion
    
    //#region writeZipFile(zipFile,entry,exportBasePath) 将压缩包中指定内容输出到目标根路径中
    /**
     * 将压缩包中指定内容输出到目标根路径中
     * @param zipf       压缩包对象
     * @param entry      压缩包内部元素
     * @param basePath   输出根路径
     * @throws Exception 异常
     * 2024年6月19日
     * @author MBG
     */
    public static void writeZipFile(ZipFile zipf,ZipEntry entry,String basePath) throws Exception {
    	if(zipf==null || entry==null) {
    		return;
    	}
    	if(entry.isDirectory()) {
    		Files.createDirectories(Paths.get(basePath+File.separator+entry.getName()));
    		return;
    	}
    	InputStream          is  = null; // 压缩包读取流
    	BufferedOutputStream bos = null; // 文件写入流
    	try {
    		is = zipf.getInputStream(entry);
    		// 构建解压后的文件对象
    		File targetFile = new File(basePath+File.separator+entry.getName());
    		// 创建目标路径
    		Files.createDirectories(Paths.get(targetFile.getParentFile().getPath()));
    		// 构建文件写入流
    		bos = new BufferedOutputStream(new FileOutputStream(targetFile));
    		//#region 执行写入文件
			byte[] buffer = new byte[4096];
			int bytesRead = -1;
			while ((bytesRead = is.read(buffer)) != -1) {
				bos.write(buffer, 0, bytesRead);
			}
			//#endregion
    	}finally {
    		if(is!=null) {
    			try {
    				is.close();
    			}catch(Exception e) {}
    		}
    		if(bos!=null) {
    			try {
    				bos.flush();
    			}catch(Exception e2) {}
    			try {
    				bos.close();
    			}catch(Exception e2) {}
    		}
    	}
    }
    //#endregion
    
    //#region copyZipScream(file,os) 复制文件到压缩包中
    /**
     * 复制文件到压缩包中
     * @param iFile        源文件
     * @param os           输出流
     * @throws Exception   异常
     * 2018年6月13日
     * @author MBG
     */
    private static void copyZipScream(File iFile,OutputStream os) throws Exception {
    	FileInputStream fis = null; //文件读入流
		try {
			fis = new FileInputStream(iFile);
			byte[] buffer = new byte[2048];
			int bytesRead;
			while ((bytesRead = fis.read(buffer)) != -1) {
				os.write(buffer, 0, bytesRead);
			}
			os.flush();
		}finally {
			try {
				fis.close();
			}catch (IOException ex) {}
			fis = null;
		}
    }
    //#endregion
}









